package com.my.blog.aop;

import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.my.blog.annotation.SystemLog;
import com.my.blog.myblog.entity.Bloger;
import com.my.blog.myblog.entity.Log;
import com.my.blog.myblog.mapper.BlogerMapper;
import com.my.blog.myblog.service.LogService;
import com.my.blog.utils.ConstantUtil;
import com.my.blog.utils.UserIpUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Objects;

/***
 *
 * @Auther: MuYun
 * @Date: 2021/9/29 14:25
 * @Description： 切面类
 *
 */
@Aspect//标记为切面类
@Component
public class SystemLogAspect {

    @Resource
    private LogService logService;

    @Resource
    private BlogerMapper blogerMapper;

    //切入点，大概意思是以@SystemLog注解作为切点
    @Pointcut("@annotation(com.my.blog.annotation.SystemLog)")
    public void serviceAspect(){
    }

    //切点之后执行，认证为serviceAspect()切点
    @After("serviceAspect()")
    public void doServiceLog(JoinPoint joinPoint){
        //从request容器中获取当前请求
        HttpServletRequest request = (
                (ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())
        ).getRequest();
        Log log=new Log();
        //通过ip工具类设置ip
        log.setIp(UserIpUtil.getIp(request));
        //初始化一下
        String userType="";
        Boolean userFlag = getUserType(joinPoint);
        if(userFlag&&StpUtil.isLogin()){
            String loginID = StpUtil.getLoginId().toString();
            QueryWrapper<Bloger> wrapper=new QueryWrapper<>();
            Bloger bloger = blogerMapper.selectById(loginID);
            userType=bloger.getUsername();
        }else {
            userType="游客";
        }
        //静态方法设置用户类别
        log.setUserType(userType);
        //静态方法设置描述信息
        log.setDescription(getDescription(joinPoint));
        //如果是登陆系统的日志类型就不去获取参数
        if(!ConstantUtil.LOGIN_IN.equals(getDescription(joinPoint))){
            //根据封装的方法对象(joinPoint)获取对应参数
            log.setParam(joinPoint.getArgs()[0].toString());
        }
        //设置新增时间
        log.setAddTime(LocalDateTime.now());

        //参数不为none时，保存日志到数据库
        if(!"none".equals(log.getParam())){
            logService.save(log);
        }
    }

    /**
     * 获取当前请求方法的描述信息
     * @param joinPoint
     * @return
     */
    public static String getDescription(JoinPoint joinPoint){
        //初始化一下
        String description="";
        //获取类名
        String classname = joinPoint.getTarget().getClass().getName();
        //获取方法名
        String methodname = joinPoint.getSignature().getName();
        //获取参数数组
        Object[] args = joinPoint.getArgs();
        try {
            //通过类名反射创建对象
            Class<?> aClass = Class.forName(classname);
            //获取对象中的方法
            Method[] methods = aClass.getMethods();
            for(Method method:methods){
                //如果是该对象中的方法
                if(method.getName().equals(methodname)){
                    //获取该方法的参数类型
                    Class<?>[] parameterTypes = method.getParameterTypes();
                    //当前请求的方法参数类型长度等于原对象中方法的参数类型长度
                    if(parameterTypes.length==args.length){
                        //确认无误后，赋@SystemLog注解中的参数
                        description=method.getAnnotation(SystemLog.class).description();
                    }
                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return description;
    }

    /**
     * 获取当前请求方法的用户类型
     * @param joinPoint
     * @return
     */
    public static Boolean getUserType(JoinPoint joinPoint){
        Boolean flag=false;
        //获取类名
        String classname = joinPoint.getTarget().getClass().getName();
        //获取方法名
        String methodname = joinPoint.getSignature().getName();
        //获取参数数组
        Object[] args = joinPoint.getArgs();
        try {
            //通过类名反射创建对象
            Class<?> aClass = Class.forName(classname);
            //获取对象中的方法
            Method[] methods = aClass.getMethods();
            for(Method method:methods){
                //如果是该对象中的方法
                if(method.getName().equals(methodname)){
                    //获取该方法的参数类型
                    Class<?>[] parameterTypes = method.getParameterTypes();
                    //当前请求的方法参数类型长度等于原对象中方法的参数类型长度
                    if(parameterTypes.length==args.length){
                        /*//确认无误后，赋@SystemLog注解中的参数
                        userType=method.getAnnotation(SystemLog.class).userType();*/
                        //确认无误后，返回true
                        flag=true;
                    }
                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return flag;
    }
}
